/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package biofilter.records;

import java.text.ParseException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.*;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import biofilter.exceptions.InvalidRecordException;
import biofilter.data.Sequence.BioSequence;
import biofilter.data.*;
import biofilter.factories.BioSequenceFactory;
import biofilter.util.StringUtil;
import biofilter.records.emblxsd.*;
import biofilter.records.emblxsd.EMBLServices.Entry;
import biofilter.records.emblxsd.EMBLServices.Entry.Sequence;
import java.math.BigInteger;

/**
 *
 * @author Daniel Quest
 * @date   Jan 11 2009
 */

/**
    	* ID line contains a brief description of the contents of the file
		ID 	id 	    sequence version 	topology 	molecule 	dataclass 	division 	sequencelength (Base Pairs)
e.g. 	ID 	U40572 	SV 1 	            linear 	    mRNA 	    STD 	    HUM 	    1700 BP

    * The XX line contains no data or comments. It is used instead of blank lines to avoid confusion with the sequence data lines.

    * The AC (Accession Number) line lists the accession numbers associated with this entry.

    * The DT (DaTe) line shows the date/release number of creation, date/release number of the last modification of the entry and the version number.

    * The DE (DEscription) lines contain general descriptive information about the sequence stored.

    * The KW (KeyWord) lines provide information which can be used to generate cross-reference indexes of the sequence entries based on functional, structural, or other categories deemed important. The keywords chosen for each entry serve as a subject reference for the sequence, and will be expanded as work with the database continues. Often several KW lines are necessary for a single entry.

    * The OS (Organism Species) line specifies the preferred scientific name of the organism which was the source of the stored sequence.

    * The OC (Organism Classification) lines contain the taxonomic classification of the source organism.

    * The RN (Reference Number) line gives a unique number to each reference citation within an entry.

    * The RC (Reference Comment) line type is an optional line type which appears if the reference has a comment.

    * The RP (Reference Position) line type is an optional line type which appears if one or more contiguous base spans of the presented sequence can be attributed to the reference in question.

    * The RX (Reference Cross-reference) line type is an optional line type which contains a cross-reference to an external citation or abstract database.

    * The RA (Reference Author) lines list the authors of the paper (or other work) cited.

    * The RT (Reference Title) lines give the title of the paper (or other work).

    * The RL (Reference Location) line contains the conventional citation information for the reference.

    * The DR (Database Cross-Reference) line cross-references other databases which contain information related to the entry in which the DR line appears.

    * The PR (PRoject) line shows the International Nucleotide Sequence Database Collaboration (INSDC) Project Identifier that has been assigned to the entry.

    * The CC lines are free text comments about the entry, and may be used to convey any sort of information thought to be useful.

    * The FH (Feature Header) lines are present only to improve readability of an entry when it is printed or displayed on a terminal screen. The lines contain no data and may be ignored by computer programs.

    * The FT (Feature Table) lines provide a mechanism for the annotation of the sequence data. Regions or sites in the sequence which are of interest are listed in the table. A complete and definitive description of the feature table is given here.

    * The SQ (SeQuence header) line marks the beginning of the sequence data and gives a summary of its content.

    * The sequence data lines has lines of code starting with two blanks. The sequence is written 60 bases per line, in groups of 10 bases separated by a blank character, beginning in position 6 of the line. The direction listed is always 5' to 3'

    * The // (terminator) line also contains no data or comments. It designates the end of an entry.

The files currently handled are ORNL specific .art files, FASTA files, and embl files.
Other file formats are not currently supported.

*/


public class EMBLRecord implements Record {

    biofilter.records.emblxsd.ObjectFactory objFactory = null;  //full path just for clerity
    AssemblyLocationElementType alet = null;
    AssemblyLocationType alt = null;
    BaseType bt = null;
    CitationType ct = null;
    DbReferenceType drt = null;
    EMBLServices embls = null;
    EntryType et = null;
    EvidenceType evt = null;
    FeatureType ft = null;
    LocationElementType let = null;
    LocationType lt = null;
    PositionType pt = null;
    ReferenceType rt = null;
    public EMBLRecord(){

    }
    
    /**
     * Setup the data objects that will be used for data transformations
     */
    public void init(){
        setupFeatures();
        setupQualifiers();
        objFactory = new ObjectFactory();
        alet = objFactory.createAssemblyLocationElementType();
        alt = objFactory.createAssemblyLocationType();
        bt = objFactory.createBaseType();
        ct = objFactory.createCitationType();
        drt = objFactory.createDbReferenceType();        
        embls = objFactory.createEMBLServices();
        et = objFactory.createEntryType();
        evt = objFactory.createEvidenceType();
        ft = objFactory.createFeatureType();
        let = objFactory.createLocationElementType();
        lt = objFactory.createLocationType();
        pt = objFactory.createPositionType();
        rt = objFactory.createReferenceType();
        return;
    }

    private String record;
    private BioSequenceFactory biosFac = null;
    private BioSequence bios = null;
    private StringUtil sute = new StringUtil();
    public String get() {
        return this.record;
    }

    public void set(String r) throws IllegalArgumentException {
       this.record = r;
    }

    //Lines like:
    //DT   23-FEB-2006 (Rel. 86, Created)
    //Are converted to lines like:
    //23-FEB-2006 (Rel. 86, Created)
    private String ripOffEMBLToken (String line){
            return line.substring(4).trim();
    }

    public String getID() {
        if (this.bios == null) parse();
        String id = "ID   " + this.et.getAccession() + "; SV " + this.et.getVersion() + "; "  + ";" ;
        return id;
    }

    //     0       1     2       3     4    5    6    
    //ID   X56734; SV 1; linear; mRNA; STD; PLN; 1859 BP.
    public void setID(String id){
        try {
            String tmp = ripOffEMBLToken(id);
            String[] tokens = sute.splitOnDelim(tmp, ";", 7);
            this.et.setAccession( tokens[0].trim() );
            this.et.setVersion( this.parseVersion(tokens[1])  );

            //this.seq.setTopology( tokens[2].trim() );
            //this.taxonomicDivision = tokens[3].trim();
            //this.created = tokens[4].trim();
            //this.dataClass = tokens[5].trim();
            //this.taxonomicDivision = tokens[6].trim();
        } catch (ParseException ex) {
            Logger.getLogger(EMBLRecord.class.getName()).log(Level.SEVERE, id, ex);
        }
    }
    public BigInteger parseVersion(String s){
        if( s.startsWith("SV") )
            return new BigInteger(s.substring(2).trim());
        else
            return new BigInteger(s);
    }

    public BioSequence getSequence(){
        if (this.bios == null) parse();
        return this.bios;
    }

    public void parse(){
        init();
        StringBuffer sb = new StringBuffer();
        for (int i=0; i<record.length(); i++){
            if(this.record.charAt(i) == '\n'){
                sb = new StringBuffer();
            }else {
                sb.append(this.record.charAt(i));
            }
        }
    }
    /**
     * deparse converts the object representation of a embl file into a embl record
     * return String of the entire embl record.
     */
    public String deparse() throws NullPointerException {
        this.record = "";  //don't call deparse if you don't intend to reset the contents of the record!
        if (this.embls == null) throw new NullPointerException("This record does not contain any information! EMBLServices is null!");
        StringBuffer sb = new StringBuffer();
        sb.append(this.getID());
        this.record = sb.toString();
        return this.record;
    }

    //mostly a method for testing...
    public void setEMBLServices(EMBLServices e){
        biosFac = new BioSequenceFactory();
        this.embls = e;
        Entry entry = (Entry) embls.getEntry().get(0);
        Sequence s = entry.getSequence();
        bios = biosFac.getBioSequence(s.getValue());
        return;
    }

    /**
     * FEATURES
     */
     HashMap features = null;
     private void setupFeatures(){
         features = new HashMap(); //NOTE the space is saved after each feature
         features.put("source","source          ");
         features.put("gene","CDS             ");
         features.put("","gene            ");
         features.put("tRNA","tRNA            ");
         features.put("ncRNA","ncRNA           ");
         features.put("rRNA","rRNA            ");
         features.put("mat_peptide","mat_peptide     ");
         features.put("repeat_region","repeat_region   ");
         features.put("misc_feature","misc_feature    ");
         features.put("tmRNA","tmRNA           ");
         features.put("rep_origin","rep_origin      ");
         features.put("sig_peptide","sig_peptide     ");
         return;
     }
      
      /**
       * QUALIFIERS
       */
       HashMap qualifiers = null;
       private void setupQualifiers(){
           qualifiers = new HashMap();
           qualifiers.put("citation", 1);
           qualifiers.put("codon_start", 1);
           qualifiers.put("EC_number", 1);
           qualifiers.put("exception", 1);
           qualifiers.put("experiment", 1);
           qualifiers.put("function", 1);
           qualifiers.put("gene", 1);
           qualifiers.put("gene_synonym", 1);
           qualifiers.put("locus_tag", 1);
           qualifiers.put("mobile_element", 1);
           qualifiers.put("mol_type", 1);
           qualifiers.put("ncRNA_class", 1);
           qualifiers.put("note", 1);
           qualifiers.put("old_locus_tag", 1);
           qualifiers.put("product", 1);
           qualifiers.put("protein_id", 1);
           qualifiers.put("pseudo", 1);
           qualifiers.put("ribosomal_slippage", 1);
           qualifiers.put("strain", 1);
           qualifiers.put("sub_strain", 1);
           qualifiers.put("translation", 1);
           qualifiers.put("transl_except", 1);
           qualifiers.put("transl_table", 1);
           qualifiers.put("db_xref", 1);  //not really an XML qualifier... deal with it later
           return;
       }
       public boolean isQualifier(String emblLine){
           String key = "";
           if (!emblLine.startsWith("FT")) return false;
           String temp = this.ripOffEMBLToken(emblLine).trim();
           if (temp.startsWith("/")) temp = temp.substring(1); //rip off any starting slash
           if (temp.contains("=\"")) {                         //rip off stuff after and including ="
               int equalPos = temp.indexOf("=\"");
           }
           if (this.qualifiers.containsKey(key)) return true;
           return false;
       }


      /**
       * LegalFeature returns true if the lineBuffer contains lines that can legally be turned into features.
       * a legal feature requires a position (or list of positions) and a feature type (e.g. CDS)
       * @param lineBuffer
       * @return
       */
/*
       public boolean legalFeature(String lineBuffer){
          String s = "";
          for(int i = 0; i < lineBuffer.size(); i++){
              s = s + lineBuffer.get(i);
          }
          if (createFeature(s) != null ){
              return true;
          }else
              return false;
      }
*/


      /**
       * Note,  A feature always represents a substring (possibly translanted) of a genome.
       * @param s - a string such as FT     CDS
       * 
       */
      public String createFeature(String s){
/*          try{
            String sprime = "";

            if(s.startsWith(FTSOURCE) ){
                sprime = ripOffToken(s, FTSOURCE);
                l = new SourceFeature(sprime, bios);
            }else if(s.startsWith(FTCDS) ){
                sprime = ripOffToken(s, FTCDS);
                l = new CDSFeature(sprime, bios);
            }else if(s.startsWith(FTGENE) ){
                sprime = ripOffToken(s, FTGENE);
                l = new GeneFeature(sprime, bios);
            }else if(s.startsWith(FTTRNA) ){
                sprime = ripOffToken(s, FTTRNA);
                l = new TRNAFeature(sprime, bios);
            }else if(s.startsWith(FTNCRNA) ){
                sprime = ripOffToken(s, FTNCRNA);
                l = new NCRNAFeature(sprime, bios);
            }else if(s.startsWith(FTRRNA) ){
                sprime = ripOffToken(s, FTRRNA);
                l = new RRNAFeature(sprime, bios);
            }else if(s.startsWith(FTMATPEPTIDE) ){
                sprime = ripOffToken(s, FTMATPEPTIDE);
                l = new MatPeptideFeature(sprime, bios);
            }else if(s.startsWith(FTREPEATREGION) ){
                sprime = ripOffToken(s, FTREPEATREGION);
                l = new RepeatFeature(sprime, bios);
            }else if(s.startsWith(FTMISCFEATURE) ){
                sprime = ripOffToken(s, FTMISCFEATURE);
                l = new MiscFeature(sprime, bios);
            }else if(s.startsWith(FTTMRNA) ){
                sprime = ripOffToken(s, FTTMRNA);
                l = new TMRNAFeature(sprime, bios);
            }else if(s.startsWith(FTREPORGIN) ){
                sprime = ripOffToken(s, FTREPORGIN);
                l = new RepOrginFeature(sprime, bios);
            }else if(s.startsWith(FTSIGPEPTIDE) ){
                sprime = ripOffToken(s, FTSIGPEPTIDE);
                l = new SigPeptideFeature(sprime, bios);
            }
            return l;
          }catch(ParseException e){
              System.err.println(e);
          }
 */
          return null;
      }
    private void parseEMBL(String emblNamePath){
        String mode = "header";
/*
        try{
            
            BufferedReader in = new BufferedReader(new FileReader(emblNamePath));
            fileBuffer.push( in.readLine() );
            while( fileBuffer.empty() == false ){
                if( ff.legal(fileBuffer)  == true ){
                    mode = "features";
                    fileBuffer.pop();
                }else if( qf.legal(fileBuffer) == true ){
                    mode = "qualifiers";
                    fileBuffer.pop();
                }else {
                    System.err.println("EMBL parser can not handle the following line(parseEMBL): " + fileBuffer.peek() );
                    fileBuffer.pop();
                }
                fileBuffer.push( in.readLine() );
            }
        }catch(FileNotFoundException e){
            System.out.println("File not found" + e);
        }catch(IOException ioe){
            System.out.println("Exception while reading the file " + ioe);
        }
*/
    }
}
